#include "CodeMaker.h"
#include "Leave.h"
#include <stdio.h>
#include <assert.h>


std::ofstream CodeMaker::code;
int CodeMaker::labelCounter = 0;

void CodeMaker::setOutput()
{
    CodeMaker::code.open("xxx.code", std::ios::out);
}

int CodeMaker::getLabelingNr(){
    return CodeMaker::labelCounter++;
}

void CodeMaker::makeCode(){
}

void CodeMaker::makePROG(ParseTree* tree){
    ParseTree* DECLS      = tree->getFirstChild();
    ParseTree* STATEMENTS = DECLS->getNextSibling();
     
    DECLS->makeCode();
    STATEMENTS->makeCode();

    code << " STP ";
}

void CodeMaker::makeDECLS(ParseTree* tree){
//DECLS ::= DECL ; DECLS
    ParseTree* DECL       =      tree->getFirstChild();
    ParseTree* semicolon  =      DECL->getNextSibling();//discard
    ParseTree* DECLS      = semicolon->getNextSibling();
     
    DECL->makeCode();
    DECLS->makeCode();
}

void CodeMaker::makeDECLSe(ParseTree* tree){}//DECLS ::= e

void CodeMaker::makeDECL(ParseTree* tree){
//DECL ::= int ARRAY identifier
    ParseTree* integer  =           tree->getFirstChild();//discard
    ParseTree* ARRAY    =        integer->getNextSibling();
    Leave* identifier   = (Leave*) ARRAY->getNextSibling();

    code << " DS " << "$" << identifier->getLexem();
    ARRAY->makeCode();
}

void CodeMaker::makeARRAY(ParseTree* tree){
//ARRAY ::= [ integer ]
    ParseTree* SQ_BR_OPEN  =                tree->getFirstChild();//discard
    Leave* integer         = (Leave*) SQ_BR_OPEN->getNextSibling();
    ParseTree* SQ_BR_CLOSE =             integer->getNextSibling();//discard

    code << " " << integer->getValue();
}

void CodeMaker::makeARRAYe(ParseTree* tree){
//ARRAY ::= e
    code << " 1 ";
}

void CodeMaker::makeSTATEMENTS(ParseTree* tree){
//STATEMENTS ::= STATEMENT ; STATEMENTS
    ParseTree* STATEMENT  =       tree->getFirstChild();
    ParseTree* semicolon  =  STATEMENT->getNextSibling();//discard
    ParseTree* STATEMENTS =  semicolon->getNextSibling();

    STATEMENT->makeCode();
    STATEMENTS->makeCode();
}

void CodeMaker::makeSTATEMENTSe(ParseTree* tree){
//STATEMENTS ::= e
    code << " NOP";
}

void CodeMaker::makeSTATEMENT_ASSIGN(ParseTree* tree){
//STATEMENT ::= identifier INDEX = EXP
    Leave* identifier = (Leave*) tree->getFirstChild();
    ParseTree* INDEX  =    identifier->getNextSibling();
    ParseTree* assign =         INDEX->getNextSibling();//discard
    ParseTree* EXP    =        assign->getNextSibling();

    EXP->makeCode();
    code << " LA $" << identifier->getLexem();
    INDEX->makeCode();
    code << " STR ";
}

void CodeMaker::makeSTATEMENT_PRINT(ParseTree* tree){
//STATEMENT ::= PRINT ( EXP )
    ParseTree* PRINT    =    tree->getFirstChild();
    ParseTree* BR_OPEN  =   PRINT->getNextSibling();//discard
    ParseTree* EXP      = BR_OPEN->getNextSibling();
    ParseTree* BL_CLOSE =     EXP->getNextSibling();//discard

    EXP->makeCode();
    code << " PRI ";
}

void CodeMaker::makeSTATEMENT_READ(ParseTree* tree){
//STATEMENT ::= read ( identifier INDEX ) 
    ParseTree* read     =             tree->getFirstChild();
    ParseTree* BR_OPEN  =             read->getNextSibling();//discard
    Leave* identifier   = (Leave*) BR_OPEN->getNextSibling();
    ParseTree* INDEX    =       identifier->getNextSibling();
    ParseTree* BR_CLOSE =            INDEX->getNextSibling();//discard

    code << " REA ";
    code << " LA $" << identifier->getLexem();

    INDEX->makeCode();
    
    code << " STR ";
}

void CodeMaker::makeSTATEMENT_CURLY_BR_OPEN(ParseTree* tree){
//STATEMENT ::= { STATEMENTS }
    ParseTree* CURLY_BR_OPEN  =          tree->getFirstChild();//discard
    ParseTree* STATEMENTS     = CURLY_BR_OPEN->getNextSibling();
    ParseTree* CURLY_BR_CLOSE =    STATEMENTS->getNextSibling();//discard

    STATEMENTS->makeCode();
}

void CodeMaker::makeSTATEMENT_IF(ParseTree* tree){
//STATEMENT ::= if ( EXP ) STATEMENT else STATEMENT
    ParseTree* IF         =       tree->getFirstChild();
    ParseTree* BR_OPEN    =         IF->getNextSibling();//discard
    ParseTree* EXP        =    BR_OPEN->getNextSibling();
    ParseTree* BR_CLOSE   =        EXP->getNextSibling();//discard
    ParseTree* STATEMENT1 =   BR_CLOSE->getNextSibling();
    ParseTree* ELSE       = STATEMENT1->getNextSibling();//discard
    ParseTree* STATEMENT2 =       ELSE->getNextSibling();

    EXP->makeCode();

    int nr1 = CodeMaker::getLabelingNr();
    int nr2 = CodeMaker::getLabelingNr();

    code << " JIN #marke" << nr1;
    STATEMENT1->makeCode();//attention: switched order of statements
    code << " JMP #marke" << nr2;
    code << " #marke" << nr1 << " NOP ";
    STATEMENT2->makeCode();//attention: switched order of statements
    code << " #marke" << nr2 << " NOP ";
}

void CodeMaker::makeSTATEMENT_WHILE(ParseTree* tree){
//STATEMENT ::= while ( EXP ) STATEMENT
    ParseTree* WHILE     =     tree->getFirstChild();
    ParseTree* BR_OPEN   =    WHILE->getNextSibling();//discard
    ParseTree* EXP       =  BR_OPEN->getNextSibling();
    ParseTree* BR_CLOSE  =      EXP->getNextSibling();//discard
    ParseTree* STATEMENT = BR_CLOSE->getNextSibling();

    int nr1 = CodeMaker::getLabelingNr();
    int nr2 = CodeMaker::getLabelingNr();

    code << " #marke" << nr1 << " NOP ";
    EXP->makeCode();
    code << " JIN #marke" << nr2;
    STATEMENT->makeCode();
    code << " JMP #marke" << nr1;
    code << " #marke" << nr2 << " NOP ";
}

void CodeMaker::makeEXP(ParseTree* tree){
//EXP ::= EXP2 OP_EXP
    ParseTree* EXP2   = tree->getFirstChild();
    ParseTree* OP_EXP = EXP2->getNextSibling();

    if(OP_EXP->getType() == TYPE_NO){
        EXP2->makeCode();
    } else if(OP_EXP->getFirstChild()->getType() == TYPE_OP_GREATER){
        OP_EXP->makeCode();
        EXP2->makeCode();
        code << " LES ";
    } else if(OP_EXP->getFirstChild()->getType() == TYPE_OP_UNEQUAL){
        EXP2->makeCode();
        OP_EXP->makeCode();
        code << " NOT ";
    } else { 
        EXP2->makeCode();
        OP_EXP->makeCode();
    }
}

void CodeMaker::makeEXP2_BR_OPEN(ParseTree* tree){
//INDEX ::= ( EXP )
    ParseTree* BR_OPEN   =    tree->getFirstChild();//discard
    ParseTree* EXP       = BR_OPEN->getNextSibling();
    ParseTree* BR_CLOSE  =     EXP->getNextSibling();//discard

    EXP->makeCode();
}

void CodeMaker::makeEXP2_IDENTIFIER(ParseTree* tree){
//EXP ::= identifier INDEX
    Leave* identifier = (Leave*) tree->getFirstChild();
    ParseTree* INDEX  =    identifier->getNextSibling();

    code << " LA $" << identifier->getLexem();
    INDEX->makeCode();
    code << " LV ";
}

void CodeMaker::makeEXP2_INTEGER(ParseTree* tree){
//EXP ::= integer
    Leave* integer = (Leave*) tree->getFirstChild();

    code << " LC " << integer->getValue();
}

void CodeMaker::makeEXP2_MINUS(ParseTree* tree){
//EXP ::= - EXP2
    ParseTree* MINUS = tree->getFirstChild();
    ParseTree* EXP2 = MINUS->getNextSibling();
    
    code << " LC 0";
    EXP2->makeCode();
    code << " SUB ";
}

void CodeMaker::makeEXP2_EXCL(ParseTree* tree){
//EXP ::= ! EXP2
    ParseTree* EXCL = tree->getFirstChild();//discard
    ParseTree* EXP2 = EXCL->getNextSibling();

    EXP2->makeCode();
    code << " NOT ";
}

void CodeMaker::makeINDEX(ParseTree* tree){
//INDEX ::= [ EXP ]
    ParseTree* SQ_BR_OPEN  =       tree->getFirstChild();//discard
    ParseTree* EXP         = SQ_BR_OPEN->getNextSibling();
    ParseTree* SQ_BR_CLOSE =        EXP->getNextSibling();//discard

    EXP->makeCode();
    code << " ADD ";
}

void CodeMaker::makeINDEXe(ParseTree* tree){} //INDEX ::= e

void CodeMaker::makeOP_EXP(ParseTree* tree){
//OP_EXP ::= OP EXP
    ParseTree* OP  = tree->getFirstChild();
    ParseTree* EXP =   OP->getNextSibling();

    EXP->makeCode();
    OP->makeCode();
}

void CodeMaker::makeOP_EXPe(ParseTree* tree){} //OP_EXP ::= e

void CodeMaker::makeOP_PLUS   (ParseTree* tree){ code << " ADD "; }

void CodeMaker::makeOP_MINUS  (ParseTree* tree){ code << " SUB "; }

void CodeMaker::makeOP_MULT   (ParseTree* tree){ code << " MUL "; }

void CodeMaker::makeOP_DIV    (ParseTree* tree){ code << " DIV "; }

void CodeMaker::makeOP_LESS   (ParseTree* tree){ code << " LES "; }

void CodeMaker::makeOP_GREATER(ParseTree* tree){                  }//special case, see transparancies

void CodeMaker::makeOP_EQUAL  (ParseTree* tree){ code << " EQU "; }

void CodeMaker::makeOP_UNEQUAL(ParseTree* tree){ code << " EQU "; }//special case, see transparancies

void CodeMaker::makeOP_AND    (ParseTree* tree){ code << " AND "; }
